package org.pp.openapi.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.pp.openapi.common.ApiException;
import org.pp.openapi.common.ApiUser;
import org.pp.openapi.consts.CacheKeys;
import org.pp.openapi.consts.LogType;
import org.pp.openapi.domain.ApiEvent;
import org.pp.openapi.domain.ApiModel;
import org.pp.openapi.sql.OpenDataSqlProvider;
import org.pp.openapi.service.*;
import org.pp.openapi.mapper.OpenDataMapper;
import org.pp.openapi.vo.*;
import org.pp.openapi.utils.JsUtil;
import org.pp.openapi.utils.StrUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class DbServiceImpl implements IDbService {
    @Autowired
    private OpenDataMapper openDataMapper;
    @Autowired
    private IApiLoaderService apiLoaderService;
    @Autowired
    private IOpenApiService openApiService;

    /**
     * 查询一条SQL
     * @param sql
     * @return
     */
    @Override
    public List<Map> query(String sql){
        return openDataMapper.query(sql);
    }

    /**
     * 联表分页查询
     * @param query
     * @return
     */
    @Override
    public List<Map> query(RelQuery query){
        Map<String,Object> sqlMap = queryMap(query);

        PageHelper.clearPage();
        if(query.getPager() != null) {
            PageHelper.startPage(query.getPager().getPage(), query.getPager().getLimit(), query.getPager().getOrder());
        }else{
            PageHelper.startPage(1, 10);
        }

        return openDataMapper.selectByMap(sqlMap);
    }

    /**
     * 联表单条查询
     * @param query
     * @return
     */
    @Override
    public Map find(RelQuery query){
        Map<String,Object> sqlMap = queryMap(query);

        return openDataMapper.findByMap(sqlMap);
    }

    /**
     * 单表分页查询
     * @param table
     * @return
     */
    @Override
    public List<Map> query(TableQuery table){
        Map<String,Object> sqlMap = queryMap(table);

        PageHelper.clearPage();
        if(table.getPager() != null) {
            PageHelper.startPage(table.getPager().getPage(), table.getPager().getLimit(), table.getPager().getOrder());
        }else{
            PageHelper.startPage(1, 10);
        }
        return openDataMapper.selectByMap(sqlMap);
    }

    /**
     * 单表单条查询
     * @param table
     * @return
     */
    @Override
    public Map find(TableQuery table){
        Map<String,Object> sqlMap = queryMap(table);
        return openDataMapper.findByMap(sqlMap);
    }

    /**
     * 带数据权限的查询
     * @param query
     * @return
     */
    private Map<String,Object> queryMap(TableQuery query){
        //超级管理员不做权限验证
        Map<String,Object>  sqlMap = query.getParams();
        sqlMap.put(OpenDataSqlProvider.SQL_FIELD, query.getSql(null));
        if(openApiService.isAdmin()){
            return sqlMap;
        }

        //判断模型是否存在
        String table = null;
        if(query instanceof RelQuery){
            table = ((RelQuery)query).getMainTable().getTable();
        }else{
            table = query.getTable();
        }
        ApiModel model = apiLoaderService.getModel(table);
        if(model == null){
            throw new ApiException("数据模型 "+table+" 未定义");
        }

        //加入数据权限
        if(StrUtil.isNotEmpty(model.getUserScope())){
            sqlMap.put(OpenDataSqlProvider.SQL_FIELD, query.getSql(model.getUserScope()));
            query.addParams("_userId", openApiService.getLoginUser().getUserId());
        }else if(StrUtil.isNotEmpty(model.getOrgScope())){
            sqlMap.put(OpenDataSqlProvider.SQL_FIELD, query.getSql(model.getOrgScope()));
            String[] orgIds = openApiService.getSubDeptIds(openApiService.getLoginUser().getDeptId());
            query.addParams("_orgId", "'"+StrUtil.join(orgIds, "','")+"'");
        }else{
            sqlMap.put(OpenDataSqlProvider.SQL_FIELD, query.getSql(null));
        }
        return sqlMap;
    }

    /**
     * 填充关联值,目前只支持二级结构
     * 如："userId":"@USER.id"
     * @param retMap
     * @param dataMap
     */
    @Override
    public void fillData(Map<String, Map> retMap,  Map<String, Object> dataMap){
        for(String p:dataMap.keySet()){
            if(dataMap.get(p) == null){
                continue;
            }

            String v = dataMap.get(p).toString();
            if(!v.startsWith("@")) {
                continue;
            }

            //自动填充参数
            String[] ps = v.split("\\.");
            String pname = ps[0].substring(1);
            if(!retMap.containsKey(pname)){
                throw new ApiException("保存数据的业务逻辑异常");
            }
            dataMap.put(p, retMap.get(pname).get(ps[1]));
        }
    }

    /**
     * 插入数据，全根据主键和唯一字段进行重复性判断，如果override=true会自动进行更新
     * @param table TableData
     * @param override 存在是否覆盖
     * @return
     */
    @Override
    public int insertData(TableData table, boolean override){
        ApiModel model = apiLoaderService.getCacheObject(CacheKeys.CACHE_MODEL+table.getTable());
        if(model == null){
            throw new ApiException("数据模型未定义");
        }

        Map<String, Object> dataMap = table.getData();

        //用主键判断数据是否存在
        String id = dataMap.getOrDefault(model.getPkField(), "").toString();
        if(StringUtils.isNotEmpty(id)){
            List<Map> old = openDataMapper.query("SELECT * FROM " + table.getTable() + " WHERE `"+model.getPkField()+"` = '"+id+"'");
            if(old != null && !old.isEmpty() && !override){
                throw new ApiException("数据已存在");
            }else{
                //允许覆盖则更新
                return updateData(table);
            }
        }

        //唯一性验证
        if(model.getUniqueField() != null && model.getUniqueField().length >0) {
            Map<String, Object> findMap = new HashMap<>();
            StringBuilder sb = new StringBuilder("SELECT * FROM ");
            sb.append(table.getTable()).append(" WHERE 1 ");
            for(String f:model.getUniqueField()){
                sb.append(" AND ").append(f).append(" = #{").append(f).append("}");
                findMap.put(f, table.getData(f));
            }
            findMap.put(OpenDataSqlProvider.SQL_FIELD, sb.toString());
            Map oldData = openDataMapper.findByMap(findMap);
            if(oldData != null && !oldData.isEmpty()){
                if(override) {
                    //允许覆盖则更新
                    table.addData(model.getPkField(), oldData.get(model.getPkField()));
                    return updateData(table);
                }else {
                    log.error("table {} unique check failed:", table.getTable(), findMap);
                    throw new ApiException(table.getTable() + " 存在重复数据");
                }
            }
        }

        int rows = 0;
        // 前置操作
        if(!table.getIgnore()){
            rows += runEvent(model.getId() + "_BI", dataMap);
        }

        //保存数据
        dataMap.put(OpenDataSqlProvider.TABLE_FIELD, table.getTable());
        rows +=openDataMapper.insertMap(dataMap);

        //记录日志
        log(model.getId(), LogType.INSERT.getCode(), table.getData(model.getPkField()).toString(), dataMap);

        // 后置操作
        if(!table.getIgnore()) {
            rows += runEvent(model.getId() + "_AI", dataMap);
        }
        return rows;
    }

    /**
     * 更新数据
     * @param table
     * @return
     */
    @Override
    public int updateData(TableData table){
        ApiModel model = apiLoaderService.getCacheObject(CacheKeys.CACHE_MODEL+table.getTable());
        if(model == null){
            throw new ApiException("数据模型未定义:" + table.getTable());
        }

        Object id = table.getData(model.getPkField());
        if(StrUtil.isNull(id)){
            throw new ApiException("更新数据时主键不能为空:" + table.getTable());
        }

        Map<String, Object> dataMap = table.getData();

        int rows = 0;
        // 前置操作
        if(!table.getIgnore()) {
            rows += runEvent(model.getId() + "_BU", dataMap);
        }

        //保存数据
        dataMap.put(OpenDataSqlProvider.TABLE_FIELD, table.getTable());
        dataMap.put(OpenDataSqlProvider.PK_FIELD, model.getPkField());
        rows +=openDataMapper.updateMap(dataMap);

        //记录日志
        log(model.getId(), LogType.UPDATE.getCode(), table.getData(model.getPkField()).toString(), dataMap);

        // 后置操作
        if(!table.getIgnore()) {
            rows += runEvent(model.getId() + "_AU", dataMap);
        }
        return rows;
    }

    /**
     * 删除数据
     * @param table
     * @return
     */
    @Override
    public int deleteData(TableQuery table){
        ApiModel model = apiLoaderService.getCacheObject(CacheKeys.CACHE_MODEL+table.getTable());
        if(model == null){
            throw new ApiException("数据模型未定义");
        }

        table.setColumn("*");

        //查出原始数据进行事件处理
        List<Map> dataList = query(table);
        if(dataList == null || dataList.size()  == 0){
            return 0;
        }

        int rows = 0;
        String ids = dataList.stream().map(d->d.get(model.getPkField()).toString()).collect(Collectors.joining(","));

        //删除业务的前置操作
        if(!table.getIgnore()) {
            rows += runEvent(model.getId() + "_BD", ids);
        }

        String sql = "DELETE FROM " + table.getName(true) + " WHERE " + table.parseWhere(table);
        Map<String, Object> sqlMap = table.getData();
        sqlMap.put(OpenDataSqlProvider.SQL_FIELD, sql);
        rows += openDataMapper.deleteByMap(sqlMap);

        //记录日志
        log(model.getId(), LogType.DELETE.getCode(), null, ids);

        //删除业务的后置操作
        if(!table.getIgnore()) {
            rows += runEvent(model.getId() + "_AD", ids);
        }

        return rows;
    }

    /**
     * 触发模型事件
     * @param tableAction
     * @param data
     * @return
     */
    private int runEvent(String tableAction, Object data){
        ApiEvent event = apiLoaderService.getCacheObject(CacheKeys.CACHE_EVENT+tableAction);
        if(event == null) {
            return 0;
        }
        String sqlStr = JsUtil.exec(event.getContent(), data);
        if(sqlStr == null){
            return 0;
        }

        int rows = 0;
        sqlStr = sqlStr.trim();
        if(sqlStr.startsWith("[")){
            JSONArray sqlArr = JSONArray.parse(sqlStr);
            for(int i=0;i<sqlArr.size();i++){
                rows += openDataMapper.exec(sqlArr.getString(i));
            }
        }else{
            String[] sqls = sqlStr.split(";");
            for(String sql:sqls){
                rows += openDataMapper.exec(sql);
            }
        }

        //记录日志
        log(event.getModelId(), LogType.EVENT.getCode(), event.getId().toString(), data);
        return rows;
    }

    /**
     * 记录日志
     * @return
     */
    @Override
    public void log(Long modelId, String type, String key, Object data){
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("model_id", modelId);
        dataMap.put("log_type", type);
        dataMap.put("log_key", key);
        if(data != null){
            String dataStr;
            if(data instanceof String){
                dataStr = data.toString();
            }else{
                dataStr = JSON.toJSONString(data);
            }
            dataMap.put("log_data", dataStr.length()<500 ? dataStr : dataStr.substring(0, 499));
        }
        dataMap.put("log_time", new Date());
        ApiUser user = openApiService.getLoginUser();
        dataMap.put("user_id", user.getUserId());
        dataMap.put("user_name", user.getUsername());
        dataMap.put(OpenDataSqlProvider.TABLE_FIELD, "api_log");
        openDataMapper.insertMap(dataMap);
    }
}
